#include "LUNA_SoundManager.h"
#include "LUNA_ISoundFactory.h"
#include "LUNA_ISound.h"

#include "aldlist.h"
#include "Framework.h"

using namespace LUNA;

SoundManager::SoundManager() :
mOpenALInitialized( false ),
mVorbisInitialized( false ),
mVolume( 1.0f )
{
}

SoundManager::~SoundManager()
{
}

void SoundManager::update()
{
	for( SoundVector::iterator iter = mSounds.begin();
		iter != mSounds.end();
		++iter )
	{
		(*iter)->update();
	}
}

void SoundManager::initialize()
{
	initializeOpenAL();
}

void SoundManager::shutdown()
{
	shutdownOpenAL();
}

void SoundManager::initializeOpenAL()
{
	ALFWInit();

	initializeVorbis();

	ALDeviceList *deviceList = new ALDeviceList();
	if( deviceList && deviceList->GetNumDevices() )
	{
		// TODO: make device queryable/selectable
		// for now just pick the first device
		mDevice = alcOpenDevice( deviceList->GetDeviceName( 0 ) );
		if( mDevice )
		{
			mContext = alcCreateContext( mDevice, 0 );
			if( mContext )
			{
				alcMakeContextCurrent( mContext );
			}
			else
			{
				alcCloseDevice( mDevice );
			}
		}
		else
		{
			THROW_RT("unable to create open device");
		}
	}
	else
	{
		THROW_RT("failed to get device list");
	}
	delete deviceList;
	
	mOpenALInitialized = true;
}

void SoundManager::shutdownOpenAL()
{
	shutdownVorbis();

	ALCcontext *context = 0;
	ALCdevice *device = 0;

	context = alcGetCurrentContext();
	device = alcGetContextsDevice( context );
	
	alcMakeContextCurrent( 0 );
	alcDestroyContext( context );
	alcCloseDevice( device );
	mOpenALInitialized = false;
}

ISound *SoundManager::createSound( const std::string &filename,
	const std::string &soundType )
{
	ISound *sound = getFactory( soundType )->createSound( filename );
	mSounds.push_back( sound );

	sound->_setMasterVolume( mVolume );

	return sound;
}

void SoundManager::destroySound( ISound *sound )
{
	assert( sound && __FUNCTION__ );
	SoundVector::iterator iter = std::find( mSounds.begin(), mSounds.end(), sound );
	if( iter != mSounds.end() )
	{
		mSounds.erase( iter );
		getFactory( sound->getType() )->destroySound( sound );
	}
	else
	{
		THROW_RT("tried to destroy a sound that isn't registered;");
	}	
}

void SoundManager::registerSoundFactory( ISoundFactory *factory )
{
	SoundFactoryMap::iterator iter = 
		mFactories.find( factory->getFactoryType() );
	if( iter != mFactories.end() )
	{
		THROW_RT( "factory with that type name already registered; type: " \
			<< factory->getFactoryType() );
	}

	mFactories[factory->getFactoryType()] = factory;
}

void SoundManager::unregisterSoundFactory( ISoundFactory *factory )
{	
	SoundFactoryMap::iterator iter = 
		mFactories.find( factory->getFactoryType() );
	if( iter == mFactories.end() )
	{
		THROW_RT( "factory with that type name not registered; type: " \
			<< factory->getFactoryType() );
	}

	mFactories.erase( iter );
}

bool SoundManager::isSoundFactoryAvailable( const std::string &name )
{
	SoundFactoryMap::iterator iter = mFactories.find( name );
	return( iter != mFactories.end() );
}

StringList SoundManager::listAvailableSoundFactories() const
{
	StringList out;
	for( SoundFactoryMap::const_iterator iter = mFactories.begin();
		iter != mFactories.end();
		++iter )
	{
		out.push_back( iter->first );
	}
	return out;
}

VorbisSystem &SoundManager::getVorbisSystem()
{
	return mVorbis;
}

float SoundManager::getVolume() const
{
	return mVolume;
}

void SoundManager::setVolume( float volume )
{
	mVolume = volume;

	for( SoundVector::iterator iter = mSounds.begin();
		iter != mSounds.end();
		++iter )
	{
		(*iter)->_setMasterVolume( volume );
	}
}

ISoundFactory *SoundManager::getFactory( const std::string &name )
{	
	SoundFactoryMap::iterator iter = mFactories.find( name );
	if( iter == mFactories.end() )
	{
		THROW_RT( "factory with that type does not exist; type: " << name );
	}

	return iter->second;
}

bool SoundManager::isOpenALInitialized() const
{
	return mOpenALInitialized;
}

void SoundManager::initializeVorbis()
{
	mVorbis.vorbisDLLHandle = LoadLibrary( "vorbisfile.dll" );
	if( !mVorbis.vorbisDLLHandle )
	{
		throw std::runtime_error( "failed to load vorbisfile.dll" );
	}

	mVorbis.fn_ov_clear = 
		(LPOVCLEAR)GetProcAddress( mVorbis.vorbisDLLHandle, "ov_clear" );
	mVorbis.fn_ov_read = 
		(LPOVREAD)GetProcAddress( mVorbis.vorbisDLLHandle, "ov_read" );
	mVorbis.fn_ov_pcm_total = 
		(LPOVPCMTOTAL)GetProcAddress( mVorbis.vorbisDLLHandle, "ov_pcm_total" );
	mVorbis.fn_ov_info = 
		(LPOVINFO)GetProcAddress( mVorbis.vorbisDLLHandle, "ov_info" );
	mVorbis.fn_ov_comment = 
		(LPOVCOMMENT)GetProcAddress( mVorbis.vorbisDLLHandle, "ov_comment" );
	mVorbis.fn_ov_open_callbacks = 
		(LPOVOPENCALLBACKS)GetProcAddress( mVorbis.vorbisDLLHandle, "ov_open_callbacks" );

	if( !(mVorbis.fn_ov_clear && mVorbis.fn_ov_read && mVorbis.fn_ov_pcm_total &&
		mVorbis.fn_ov_info && mVorbis.fn_ov_comment && mVorbis.fn_ov_open_callbacks) )
	{
		throw std::runtime_error( "failed to initialize vorbis" );
	}

	mVorbisInitialized = true;
}

void SoundManager::shutdownVorbis()
{
	mVorbisInitialized = false;

	FreeLibrary( mVorbis.vorbisDLLHandle );
	mVorbis.vorbisDLLHandle = 0;
}

bool SoundManager::isVorbisInitialized() const
{
	return mVorbisInitialized;
}